import io.jsonwebtoken.*
import io.jsonwebtoken.io.Decoders
import io.jsonwebtoken.security.SignatureException
import io.vertx.core.Vertx
import io.vertx.core.http.HttpMethod
import io.vertx.core.http.HttpServerRequest
import io.vertx.core.json.DecodeException
import io.vertx.core.json.Json
import io.vertx.core.json.JsonObject
import java.math.BigInteger
import java.security.Key
import java.security.KeyFactory
import java.security.PublicKey
import java.security.spec.KeySpec
import java.security.spec.RSAPublicKeySpec


class ChatServer {
    companion object {
        var count1: Long = 0
        val errCodeInvalid = 315

        //val gson = Gson()
        val keychain = MyKeyResolver()
        val parser = Jwts.parserBuilder().setSigningKeyResolver(keychain).build()

        val vertx = Vertx.vertx()

        @JvmStatic
        fun main(args: Array<String>) {
            val our_port = 32222

            val server = vertx.createHttpServer()

            server.requestHandler { req ->
                req.bodyHandler { bod ->
                    dispatchHTTP(req, bod.toString("UTF-8"))
                }
            }.webSocketHandler { wb ->
                wb.exceptionHandler { ev ->
                    println(wb.toString() + " exceptionHandler")
                    println(ev.localizedMessage)

                    wb.close()
                }

                wb.closeHandler { }
                        .textMessageHandler { tx ->
                            println(tx)
                        }
            }
                    .listen(our_port)

            println("listening at " + our_port)
        }

        fun dispatchHTTP(exchange: HttpServerRequest, mess: String) {
            if (null != exchange) {
                if (exchange.method() == HttpMethod.GET && "/api/v1/verifyIdToken" == exchange.path()) {
                    val aa = exchange.getParam("a")
                    try {
                        val res = parser.parseClaimsJws(aa)
                        //res.header.forEach { it -> println(it) }
                        //res.body.forEach { (k, v) -> println(k + "|" + v) }
                        exchange.response().end(res.body.subject)
                    } catch (e: UnsupportedJwtException) {
                        println(e.toString())
                        println(e.localizedMessage)
                        exchange.response().setStatusCode(errCodeInvalid).end()
                    } catch (e: MalformedJwtException) {
                        println(e.toString())
                        println(e.localizedMessage)
                        exchange.response().setStatusCode(errCodeInvalid).end()
                    } catch (e: SignatureException) {
                        println(e.toString())
                        println(e.localizedMessage)
                        exchange.response().setStatusCode(errCodeInvalid).end()
                    } catch (e: IllegalArgumentException) {
                        println(e.toString())
                        println(e.localizedMessage)
                        exchange.response().setStatusCode(errCodeInvalid).end()
                    } catch (e: Exception) {
                        println("Exception")
                        println(e.toString())
                        println(e.localizedMessage)
                        //e.printStackTrace()
                        exchange.response().setStatusCode(errCodeInvalid).end()
                    }
                } else if (exchange.method() == HttpMethod.POST && "/api/v1/updateKeys" == exchange.path()) {
                    try {
                        val a = Json.decodeValue(mess) as JsonObject
                        val cleanup = a.getInteger("cleanup")
                        val kid = a.getString("kid")
                        val N = a.getString("n")
                        val E = a.getString("e")
                        val key = keymaker(N, E)

                        if (1 == cleanup) {
                            keychain.chain.clear()
                        }
                        keychain.chain.put(kid, key)

                        exchange.response().end("OK")
                    } catch (e: DecodeException) {
                        exchange.response().setStatusCode(errCodeInvalid).end()
                    }
                } else {
                    exchange.response().close()
                }
            }
        }

    }
}

fun keymaker(N: String, E: String): PublicKey {
    val n = BigInteger(1, Decoders.BASE64URL.decode(N))
    val e = BigInteger(1, Decoders.BASE64URL.decode(E))

    val publicKeySpec: KeySpec = RSAPublicKeySpec(n, e)
    val keyFactory = KeyFactory.getInstance("RSA")

    return keyFactory.generatePublic(publicKeySpec)
}

class MyKeyResolver : SigningKeyResolverAdapter() {
    val chain = mutableMapOf<String, PublicKey>()

    override fun resolveSigningKey(header: JwsHeader<out JwsHeader<*>>?, claims: Claims?): Key {
        header?.forEach { a, b -> println(a + "|" + b) }
        println("====================================")
        claims?.forEach { a, b -> println(a + "|" + b) }
        println("====================================")

        // find an key
        chain.forEach{a->println("kid|"+a)}
        if (null == header) return super.resolveSigningKey(header, claims)

        val bb = chain.get(header.getKeyId())
        if (null == bb) return super.resolveSigningKey(header, claims)

        return bb
    }
}

class JToC(val cmd: String)